package model;

import java.io.RandomAccessFile;

import model.registro.ConversionesTiposDatos;
import model.compresion.*;
import model.compresion.artmetico.*;
import model.compresion.LZP.*;
import model.compresion.PPMC.*;
import model.extras.*;
import model.extras.bitArray.BitArrayStream;


public class CompressionManager {
	
	private static final int ordenPPMC=2;
	
	public static int ProcesarCMD(char xOperacion, char xAlgoritmo, String xRutaArchivoEntrada) throws Exception {
	
		switch(xOperacion) {
			case 'c':
				String RutaSalida=null;
				String Algoritmo=null;
				byte[] DatosTemporal=null;
				byte[] DatosComprimidos=null;
				byte[] DatosDescomprimidos=null;
				RandomAccessFile ArchivoEntrada=null;
				RandomAccessFile ArchivoSalida=null;
			    
				ArchivoEntrada = new RandomAccessFile(xRutaArchivoEntrada,"rw");
				if (ArchivoEntrada.getFilePointer()==ArchivoEntrada.length())
					return -1;
				DatosTemporal=new byte[(int)ArchivoEntrada.length()];
				for (int i=0; i<ArchivoEntrada.length(); i++)
					DatosTemporal[i]=ArchivoEntrada.readByte();
				ArchivoEntrada.close();

				String DatosEntrada = new String(DatosTemporal);
				DatosDescomprimidos=DatosEntrada.getBytes("UTF-16BE");
								
				switch(xAlgoritmo) {
					case 'a':
						DatosComprimidos=ComprimirAritmetico(DatosDescomprimidos);
						RutaSalida=FileExists.CleanPath(xRutaArchivoEntrada)+".arc";
						Algoritmo="ARITMETICO";
						break;
					case 'l':
						DatosComprimidos=ComprimirLZP(DatosDescomprimidos);
						
						RutaSalida=FileExists.CleanPath(xRutaArchivoEntrada)+".lzp";
						Algoritmo="LZP";
						break;
					case 'p':
						DatosComprimidos=ComprimirPPMC(DatosDescomprimidos);
						RutaSalida=FileExists.CleanPath(xRutaArchivoEntrada)+".ppmc";
						Algoritmo="PPMC";
						break;
					default:
						break;
				}
				if (FileExists.verifyPath(RutaSalida))
					RutaSalida=FileExists.AddCopyOf(RutaSalida);
				ArchivoSalida = new RandomAccessFile(RutaSalida,"rw");
				for (int i=0; i<DatosComprimidos.length; i++)
					ArchivoSalida.writeByte(DatosComprimidos[i]);					
				ArchivoSalida.close();
				System.out.println("Se comprimió el archivo \""+xRutaArchivoEntrada+"\" utilizando el algoritmo \""+Algoritmo+"\". Grado de compresión logrado: "+(100-(DatosComprimidos.length*100)/DatosDescomprimidos.length)+"%.");	
				System.out.println("Se generó el archivo \""+RutaSalida+"\".");
				break;
			case 'd':
				RutaSalida=null;
				Algoritmo=null;
				DatosComprimidos=null;
				DatosDescomprimidos=null;
				ArchivoEntrada=null;
				ArchivoSalida=null;
				
				ArchivoEntrada = new RandomAccessFile(xRutaArchivoEntrada,"rw");
				if (ArchivoEntrada.getFilePointer()==ArchivoEntrada.length())
					return -1;
				DatosComprimidos=new byte[(int)ArchivoEntrada.length()];
				for (int i=0; i<ArchivoEntrada.length(); i++)
					DatosComprimidos[i]=ArchivoEntrada.readByte();
				ArchivoEntrada.close();
				
				switch(xAlgoritmo) {
				case 'a':
					DatosDescomprimidos=DescomprimirAritmetico(DatosComprimidos);
					Algoritmo="ARITMETICO";
					break;
				case 'l':
					DatosDescomprimidos=DescomprimirLZP(DatosComprimidos);
					Algoritmo="LZP";
					break;
				case 'p':
					DatosDescomprimidos=DescomprimirPPMC(DatosComprimidos);
					Algoritmo="PPMC";
					break;
				default:
					break;
				}
				String DatosSalida = new String(DatosDescomprimidos, 0, DatosDescomprimidos.length, "UTF-16BE");
				
				RutaSalida=FileExists.RemoveExtension(FileExists.CleanPath(xRutaArchivoEntrada));
				if (FileExists.verifyPath(RutaSalida))
					RutaSalida=FileExists.AddCopyOf(RutaSalida);
				ArchivoSalida = new RandomAccessFile(RutaSalida,"rw");
				ArchivoSalida.write(DatosSalida.getBytes());
				ArchivoSalida.close();
				System.out.println("Se descomprimió el archivo \""+xRutaArchivoEntrada+"\" utilizando el algoritmo \""+Algoritmo+"\".");
				System.out.println("Se generó el archivo \""+RutaSalida+"\".");
				break;
			default:
				break;		
		}		
	return 0;
	}
	
	public static byte[] ComprimirAritmetico(byte[] xDatosAComprimir) throws Exception {
		
		CompresorAritmetico Compresor = new CompresorAritmetico();
		TablaProbabilidades TablaP = new TablaProbabilidades();

		int pos = 0;
		for (int i=0; i< xDatosAComprimir.length;i+=2) {
			pos = ConversionesTiposDatos.ObtenerIdCaracterUTF16BE(new byte[]{xDatosAComprimir[i],xDatosAComprimir[i+1]});
			Compresor.calcularIntervalo(TablaP, pos);
			TablaP.aumentarFrecuencia(pos);
		}
		Compresor.calcularIntervalo(TablaP, 65537);//Esto finaliza la compresion
		
		return Compresor.getOutput().GetBytes();
	}
	
	public static byte[] ComprimirLZP(byte[] xDatosAComprimir) throws Exception {
		
		CompresorLZP compresor = new CompresorLZP();

		return compresor.comprimir(xDatosAComprimir);
	}
	
	public static byte[] ComprimirPPMC(byte[] xDatosAComprimir) throws Exception {
		
		CompresorPPMC Compresor = new CompresorPPMC(ordenPPMC);
		
		int pos = 0;
		for (int i=0; i< xDatosAComprimir.length;i+=2) {
			pos = ConversionesTiposDatos.ObtenerIdCaracterUTF16BE(new byte[]{xDatosAComprimir[i],xDatosAComprimir[i+1]});
			Compresor.ComprimirPPMC(pos);
		}
		Compresor.ComprimirPPMC(65537);//Esto finaliza la compresion
		
		return Compresor.Salida().getOutput().GetBytes();
	}
	
	public static byte[] DescomprimirAritmetico(byte[] xDatosADescomprimir) throws Exception {
		
		BitArrayStream Datos = new BitArrayStream(xDatosADescomprimir);
		DescompresorAritmetico Descompresor = new DescompresorAritmetico(Datos);
		TablaProbabilidades TablaP = new TablaProbabilidades();
		
		boolean eof = false;
		while (!eof){
			eof = Descompresor.descomprimirSiguientePaso(TablaP);
			TablaP.aumentarFrecuencia(Descompresor.getUltimoDescomprimido());

		}
		return Descompresor.getOut().toByteArray();
	}
	
	public static byte[] DescomprimirLZP(byte[] xDatosADescomprimir) throws Exception {

		CompresorLZP compresor = new CompresorLZP();
		
		return compresor.descomprimir(xDatosADescomprimir);
	}
	
	public static byte[] DescomprimirPPMC(byte[] xDatosADescomprimir) throws Exception {
		
		CompresorPPMC Compresor = new CompresorPPMC(ordenPPMC);
		BitArrayStream Datos = new BitArrayStream(xDatosADescomprimir);
		
		return Compresor.DescomprimirPPMC(Datos);
	}
	
}
